using UnityEngine;
using System;

namespace AudioStudio
{
    [Serializable]
    ///@brief This type represents the base for all Wwise Types that require a GUID.
    public class AudioBaseType
    {
#if UNITY_EDITOR
        public byte[] valueGuid = new byte[16];
#endif

        public int ID = 0;
        public uint GetID() { return (uint)ID; }

        public string name = string.Empty;
        public string GetName() { return name; }

        public virtual bool IsValid()
        {
            return ID != 0;
        }

        public bool Validate()
        {
            if (IsValid())
                return true;

            AkSoundEngine.LogWarning("Wwise ID has not been resolved. Consider picking a new " + GetType().Name + ".");
            return false;
        }

        protected void Verify(AKRESULT result)
        {
#if UNITY_EDITOR
            if (result != AKRESULT.AK_Success)
                AkSoundEngine.LogWarning("Unsuccessful call made on " + GetType().Name + ".");
#endif
        }
    }

    [Serializable]
    ///@brief This type represents the base for all Wwise Types that also require a group GUID, such as State and Switch.
    public class AudioBaseGroupType : AudioBaseType
    {
#if UNITY_EDITOR
        public byte[] groupGuid = new byte[16];
#endif

        public int groupID = 0;
        public uint GetGroupID() { return (uint)groupID; }

        public string groupName = string.Empty;
        public string GetGroupName() { return groupName; }

        public override bool IsValid() { return base.IsValid() && groupID != 0; }
    }

    [Serializable]
    ///@brief This type can be used to post events to the sound engine.
    public class AudioEvent : AK.Wwise.BaseType
    {
        public override WwiseObjectType WwiseObjectType { get { return WwiseObjectType.Event; } }

        private void VerifyPlayingID(uint playingId)
        {
#if UNITY_EDITOR || AUTO_TEST
            if (playingId == AkSoundEngine.AK_INVALID_PLAYING_ID)
                AkSoundEngine.LogError("Could not post event ID \"" + Id + "\". Did you make sure to load the appropriate SoundBank?");
#endif
        }

        public uint Post()
        {
            if (!IsValid())
                return AkSoundEngine.AK_INVALID_PLAYING_ID;

            uint playingId = AkSoundEngine.PostEvent(Id, AudioManager.Instance.GlobalSoundObject);
            VerifyPlayingID(playingId);
            return playingId;
        }

        public uint Post(GameObject gameObj)
        {
            if (!IsValid())
                return AkSoundEngine.AK_INVALID_PLAYING_ID;

            GameObject obj = (gameObj == null) ? AudioManager.Instance.GlobalSoundObject : gameObj;
            uint playingId = AkSoundEngine.PostEvent(Id, obj);
            VerifyPlayingID(playingId);
            return playingId;
        }

        public void Stop(int transitionDuration = 0, AkCurveInterpolation curveInterpolation = AkCurveInterpolation.AkCurveInterpolation_Linear, bool playerCtrl = false)
        {
            ExecuteAction(AudioManager.Instance.GlobalSoundObject, AkActionOnEventType.AkActionOnEventType_Stop, transitionDuration, curveInterpolation, playerCtrl);
        }

        public void Stop(GameObject gameObj, int transitionDuration = 0, AkCurveInterpolation curveInterpolation = AkCurveInterpolation.AkCurveInterpolation_Linear, bool playerCtrl = false)
        {
            ExecuteAction(gameObj, AkActionOnEventType.AkActionOnEventType_Stop, transitionDuration, curveInterpolation, playerCtrl);
        }

        public void ExecuteAction(GameObject gameObj, AkActionOnEventType actionOnEventType, int transitionDuration, AkCurveInterpolation curveInterpolation, bool playerCtrl = false)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.ExecuteActionOnEvent(Id, actionOnEventType, gameObj, transitionDuration, curveInterpolation);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to post events to the sound engine.
    public class AudioEventPC : AK.Wwise.BaseType
    {
        public override WwiseObjectType WwiseObjectType { get { return WwiseObjectType.Event; } }

        public uint ID_PC;

        public byte[] valueGuidPC;

        private void VerifyPlayingID(uint playingId)
        {
#if UNITY_EDITOR
            if (playingId == AkSoundEngine.AK_INVALID_PLAYING_ID)
                AkSoundEngine.LogError("Could not post event ID \"" + Id + "\". Did you make sure to load the appropriate SoundBank?");
#endif
        }

        public uint Post(bool playerCtrl = false)
        {
            if (!IsValid())
                return AkSoundEngine.AK_INVALID_PLAYING_ID;

            uint playingId = AkSoundEngine.PostEvent(playerCtrl ? ID_PC : Id, AudioManager.Instance.GlobalSoundObject);
            VerifyPlayingID(playingId);
            return playingId;
        }

        public uint Post(GameObject gameObj, bool playerCtrl = false)
        {
            if (!IsValid())
                return AkSoundEngine.AK_INVALID_PLAYING_ID;

            uint playingId = AkSoundEngine.PostEvent(playerCtrl ? ID_PC : Id, gameObj);
            VerifyPlayingID(playingId);
            return playingId;
        }

        public void Stop(int transitionDuration = 0, AkCurveInterpolation curveInterpolation = AkCurveInterpolation.AkCurveInterpolation_Linear, bool playerCtrl = false)
        {
            ExecuteAction(AudioManager.Instance.GlobalSoundObject, AkActionOnEventType.AkActionOnEventType_Stop, transitionDuration, curveInterpolation, playerCtrl);
        }

        public void Stop(GameObject gameObj, int transitionDuration = 0, AkCurveInterpolation curveInterpolation = AkCurveInterpolation.AkCurveInterpolation_Linear, bool playerCtrl = false)
        {
            ExecuteAction(gameObj, AkActionOnEventType.AkActionOnEventType_Stop, transitionDuration, curveInterpolation, playerCtrl);
        }

        public void ExecuteAction(GameObject gameObj, AkActionOnEventType actionOnEventType, int transitionDuration, AkCurveInterpolation curveInterpolation, bool playerCtrl = false)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.ExecuteActionOnEvent(playerCtrl ? ID_PC : Id, actionOnEventType, gameObj, transitionDuration, curveInterpolation);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to set game parameter values to the sound engine.
    public class RTPC : AK.Wwise.BaseType
    {
//         public WwiseRtpcReference WwiseObjectReference;
// 
//         public override WwiseObjectReference ObjectReference
//         {
//             get { return WwiseObjectReference; }
//             set { WwiseObjectReference = value as WwiseRtpcReference; }
//         }

        public override WwiseObjectType WwiseObjectType { get { return WwiseObjectType.GameParameter; } }

        public void SetValue(float value, GameObject gameObject)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.SetRTPCValue(Id, value, gameObject);
                Verify(result);
            }
        }

        public void SetValue(float value)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.SetRTPCValue(Id, value);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to post triggers to the sound engine.
    public class Trigger : AK.Wwise.BaseType
    {
//         public WwiseTriggerReference WwiseObjectReference;
// 
//         public override WwiseObjectReference ObjectReference
//         {
//             get { return WwiseObjectReference; }
//             set { WwiseObjectReference = value as WwiseTriggerReference; }
//         }

        public override WwiseObjectType WwiseObjectType { get { return WwiseObjectType.Trigger; } }

        public void Post(GameObject gameObject)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.PostTrigger(Id, gameObject);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to set Wwise states.
    public class State : AK.Wwise.BaseGroupType
    {
        //         public WwiseStateReference WwiseObjectReference;
        // 
        //         public override WwiseObjectReference ObjectReference
        //         {
        //             get { return WwiseObjectReference; }
        //             set { WwiseObjectReference = value as WwiseStateReference; }
        //         }

        public string groupName;

        public override WwiseObjectType WwiseObjectType { get { return WwiseObjectType.State; } }
        public override WwiseObjectType WwiseObjectGroupType { get { return WwiseObjectType.StateGroup; } }

        public void SetValue()
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.SetState(GroupId, Id);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type can be used to set switch values on gameobjects.
    public class Switch : AK.Wwise.BaseGroupType
    {
//         public WwiseSwitchReference WwiseObjectReference;
// 
//         public override WwiseObjectReference ObjectReference
//         {
//             get { return WwiseObjectReference; }
//             set { WwiseObjectReference = value as WwiseSwitchReference; }
//         }

        public string groupName
        {
            get;
            set;
        }

        public override WwiseObjectType WwiseObjectType { get { return WwiseObjectType.Switch; } }
        public override WwiseObjectType WwiseObjectGroupType { get { return WwiseObjectType.SwitchGroup; } }

        public void SetValue(GameObject gameObject)
        {
            if (IsValid())
            {
                AKRESULT result = AkSoundEngine.SetSwitch(GroupId, Id, gameObject);
                Verify(result);
            }
        }
    }

    [Serializable]
    ///@brief This type represents an auxiliary send in the master-mixer hierarchy.
    public class AuxBus : AK.Wwise.BaseType
    {
//         public WwiseAuxBusReference WwiseObjectReference;
// 
//         public override WwiseObjectReference ObjectReference
//         {
//             get { return WwiseObjectReference; }
//             set { WwiseObjectReference = value as WwiseAuxBusReference; }
//         }

        public override WwiseObjectType WwiseObjectType { get { return WwiseObjectType.AuxBus; } }
    }

    [Serializable]
    ///@brief This type can be used to load/unload soundbanks.
    public class Bank : AK.Wwise.BaseType
    {
//         public WwiseBankReference WwiseObjectReference;
// 
//         public override WwiseObjectReference ObjectReference
//         {
//             get { return WwiseObjectReference; }
//             set { WwiseObjectReference = value as WwiseBankReference; }
//         }

        public override WwiseObjectType WwiseObjectType { get { return WwiseObjectType.Soundbank; } }

        public void Load()
        {
            if (IsValid())
                AkBankManager.LoadBank(Name);
        }

        public void LoadAsync(AkCallbackManager.BankCallback callback = null)
        {
            if (IsValid())
                AkBankManager.LoadBankAsync(Name, callback);
        }

        public void Unload()
        {
            if (IsValid())
                AkBankManager.UnloadBank(Name);
        }

       // public override bool IsValid() { return (ObjectReference != null && Name != null) && ( Name.Length != 0 || base.IsValid()); }
    }

    public enum TriggerState
    {
        Enable,
        Disable
    }

    [Serializable]
    public class SoundBankParam
    {
        public bool load = true;
        public bool unload = true;
        public Bank bank;
    }

    [Serializable]
    public class StateParam
    {
        public TriggerState trigger = TriggerState.Enable;
        public State state;

        public void TriggerOnEnable()
        {
            if (this.trigger == TriggerState.Enable)
            {
                this.state.SetValue();
            }
        }

        public void TriggerOnDisable()
        {
            if (this.trigger == TriggerState.Disable)
            {
                this.state.SetValue();
            }
        }
    }

    [Serializable]
    public class SwitchParam
    {
        public TriggerState trigger = TriggerState.Enable;
        public Switch audioSwitch;
    }

    [Serializable]
    public class EffectSoundParam
    {
        public TriggerState trigger = TriggerState.Enable;
        public AudioEvent audioEvent;
        public bool stop = false;
    }

    [Serializable]
    public class SoundParam
    {
        public AudioEventPC audioEvent;
        public bool stop = false;
        public float transitionDuration = 0.2f;
    }
}

public partial class AkSoundEngine
{
    #region logger
    public static void Log(object message)
    {
        UnityEngine.Debug.Log(message);
    }

    public static void LogWarning(object message)
    {
        UnityEngine.Debug.LogWarning(message);
    }

    public static void LogError(object message)
    {
#if AUTO_TEST
        AudioStudio.AudioManager.Instance.LogData(message);
#endif
        UnityEngine.Debug.LogError(message);
    }

    public static void LogException(Exception e)
    {
        UnityEngine.Debug.LogException(e);
    }
    #endregion
}